#pragma once

#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>


struct MyTraits : public OpenMesh::DefaultTraits
{
#if 1
  typedef OpenMesh::Vec3f Point;
  typedef OpenMesh::Vec3f Normal;
#else
  typedef OpenMesh::Vec3d Point;
  typedef OpenMesh::Vec3d Normal;
#endif
};


class MyMesh : public TriangularMesh
{
	struct VertexPriority {
		VertexHandle vh_;
		float priority;

		bool operator<(const VertexPriority& oi) const //overloaded < operator
		{
			return priority > oi.priority;
		}
			
	};

public:
	enum CubeFace { kPY=0, kNY, kPZ, kPX, kNZ, kNX, kNone };

protected:
	PosGraph::Math::Plane3f m_geo_plane;
	bool m_bGeoPlane;
	OpenMesh::EPropHandleT<Point> EPropNewPoint;
	OpenMesh::EPropHandleT<bool> EPropNewPointFlag;
	OpenMesh::FPropHandleT<bool> FPropSplitTriangle;
	OpenMesh::EPropHandleT<bool> EPropCubeEdgeFlag;
	OpenMesh::FPropHandleT<CubeFace> FPropCubeFace;
	OpenMesh::VPropHandleT<Point> VPropOrgPoint;
	OpenMesh::VPropHandleT<Point> VPropOrgNormal;

	bool draw_org_data_;
	
public:
	MyMesh(void);
	~MyMesh(void);

	virtual void init();
	bool create_boundary_path(OpenMesh::VertexHandle _start_vh, OpenMesh::VertexHandle _end_vh);
	bool get_shortest_path(const VertexHandle _start_vh, const VertexHandle _end_vh, std::vector<HalfedgeHandle> &path);
	void map_onto_cube(std::vector<VertexHandle> &selected_vertices);
	void map_onto_cube_face(std::vector<HalfedgeHandle> &edge_path0, std::vector<HalfedgeHandle> &edge_path1, std::vector<HalfedgeHandle> &edge_path2, std::vector<HalfedgeHandle> &edge_path3, CubeFace _whichface);
	virtual bool save(const std::string &strFilename);
	bool load_org_data(const std::string &strFilename);
	void automatic_selection(std::vector<VertexHandle> &selected_vertices);

	void set_draw_org_data(const bool flag){ draw_org_data_ = flag; }
	bool is_draw_org_data() const { return draw_org_data_; }

	virtual void draw();

	void split_face(const FaceHandle& _fh){
		
		VertexHandle vh;

		for(FaceEdgeIter fe_it = fe_iter(_fh); fe_it; ++fe_it){
			if(property(EPropNewPointFlag, fe_it)){
				vh = split_edge(fe_it);
			}
		}

		split(_fh, vh);
	}

   VertexHandle split_edge(const EdgeHandle& _eh)
  {
    HalfedgeHandle
      heh     = halfedge_handle(_eh, 0),
      opp_heh = halfedge_handle(_eh, 1);

    HalfedgeHandle new_heh, opp_new_heh, t_heh;
    VertexHandle   vh;
    VertexHandle   vh1(to_vertex_handle(heh));
    Point          zero(0,0,0);

    // new vertex
    vh                = new_vertex( zero );

    // memorize position, will be set later
    point( vh ) = property( EPropNewPoint, _eh );


    // Re-link mesh entities
    if (is_boundary(_eh))
    {
      for (t_heh = heh;
           next_halfedge_handle(t_heh) != opp_heh;
           t_heh = opposite_halfedge_handle(next_halfedge_handle(t_heh)))
      {}
    }
    else
    {
      for (t_heh = next_halfedge_handle(opp_heh);
           next_halfedge_handle(t_heh) != opp_heh;
           t_heh = next_halfedge_handle(t_heh) )
      {}
    }

    new_heh     = new_edge(vh, vh1);
    opp_new_heh = opposite_halfedge_handle(new_heh);
    set_vertex_handle( heh, vh );

    set_next_halfedge_handle(t_heh, opp_new_heh);
    set_next_halfedge_handle(new_heh, next_halfedge_handle(heh));
    set_next_halfedge_handle(heh, new_heh);
    set_next_halfedge_handle(opp_new_heh, opp_heh);

    if (face_handle(opp_heh).is_valid())
    {
      set_face_handle(opp_new_heh, face_handle(opp_heh));
      set_halfedge_handle(face_handle(opp_new_heh), opp_new_heh);
    }

    set_face_handle( new_heh, face_handle(heh) );
    set_halfedge_handle( vh, new_heh);
    set_halfedge_handle( face_handle(heh), heh );
    set_halfedge_handle( vh1, opp_new_heh );

    // Never forget this, when playing with the topology
    adjust_outgoing_halfedge( vh );
    adjust_outgoing_halfedge( vh1 );

	return vh;
  }
  
};
